package cobots.swarm.main;

import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Random;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;


import cobots.swarm.agents.Agent;
import cobots.swarm.agents.FollowerAgent;
import cobots.swarm.agents.LeaderAgent;
import cobots.swarm.agents.PathFollowingAgent;
import cobots.swarm.agents.Target;
import cobots.swarm.props.PropertiesClass;


public class SwarmState {

	private SwarmState(){}
	public static float currentTime = 0, R = (float)PropertiesClass.getRmean();
	public static float width = (float)PropertiesClass.getWidth(), height = (float)PropertiesClass.getHeight(); 

	public static float points = 0;

	public static final Rectangle2D.Double bounds = new Rectangle2D.Double(-width/2, -height/2, width, height);
	
	
	public static ConcurrentHashMap<Short, FollowerAgent> followers = new ConcurrentHashMap<Short, FollowerAgent>();
	public static ConcurrentHashMap<Short, LeaderAgent> leaders 	= new ConcurrentHashMap<Short, LeaderAgent>(); 
	public static ConcurrentHashMap<Short, Target> targets 			= new ConcurrentHashMap<Short, Target>();
	public static ConcurrentHashMap<Short, Vector<Point2D>> paths = new ConcurrentHashMap<Short, Vector<Point2D>>(); 
	
	public static int numLeaders = PropertiesClass.getnumLeaders(); 
	public static int numFollowers = PropertiesClass.getnumFollowers();
	protected static LinkedHashMap<Integer, Float> targetAges = new LinkedHashMap<Integer, Float>();
    protected static InteractionHandler interaction = new InteractionHandler();
    
	public static InteractionHandler getInteraction() {
		return interaction;
	}
	
	public static Vector<Point2D> getPath(short leaderId) {
		Vector<Point2D> ret = new Vector<Point2D>();
		
		if (!paths.containsKey(leaderId))
			ret.add(leader(leaderId));			
		else
			ret.addAll(paths.get(leaderId));

		return ret;
	}
	
	public static void addToPath(short leaderId, Point2D point) {
		synchronized (paths) {
			if (!paths.containsKey(leaderId))
				paths.put(leaderId, new Vector<Point2D>());
			paths.get(leaderId).add(new Point2D.Double(point.getX(), point.getY()));						
		}
	}
	
	public static void clearPath(short leaderId) {
		synchronized (paths) {
			if (paths.containsKey(leaderId))			
				paths.remove(leaderId);						
		}
	}
	

	public static float getTargetAge(int id) {
		Float ret = targetAges.get(id);
		return (ret != null)? ret : 0;
	}
	
	public static void updateTargetAges() {
		for (Target t : SwarmState.targets.values()) {
			int tid = t.getId();
			if (!targetAges.containsKey(tid)) {
				targetAges.put(tid, SwarmState.currentTime);
			}
		}
	}

	public static Collection<Short> getLeaderIds() {	
		return leaders.keySet();
	}

	public static Collection<Short> getFollowerIds() {	
		return followers.keySet();
	}

	public static Collection<Short> getFreeLeaderIds() {

		Vector<Short> ret = new Vector<Short>();
		for (Short s : getLeaderIds()) 
			if (leaders.get(s).isFree())
				ret.add(s);
		return ret;
	}

	public static Collection<Short> getFreeFollowerIds() {
		Vector<Short> ret = new Vector<Short>();
		for (Short s : getFollowerIds()) 
			if (followers.get(s).isFree())
				ret.add(s);
		return ret;
	}

	public static LinkedList<FollowerAgent> getFreeFollowers() {
		LinkedList<FollowerAgent> ret = new LinkedList<FollowerAgent>();
		for (Short s : getFollowerIds()) 
			if (followers.get(s).isFree())
				ret.add(followers.get(s));
		return ret;
	}


	public static void init(int numLeaders, int numFollowers) {

		SwarmState.numLeaders = numLeaders;
		SwarmState.numFollowers = numFollowers;

		Random random = new Random(System.currentTimeMillis());

		synchronized (targets) {
			targets.clear();	
		}

		synchronized (leaders) {
			leaders.clear();

			for (int i = 0; i < numLeaders; i++) {
				LeaderAgent l;
				
				if (PropertiesClass.getPathFollowing())
					l = new PathFollowingAgent((short)i);
				else
					l = new LeaderAgent((short)i);
				
				l.setMass(200);
				l.setLocation(random.nextGaussian() * 10 - 5, random.nextGaussian() * 10 - 5);
				l.setTargetId(-1);
				leaders.put(l.getId(), l);
			}
		}	

		synchronized (followers) {
			followers.clear();

			for (int i = numLeaders; i < numLeaders+numFollowers; i++) {
				FollowerAgent l = new FollowerAgent((short)i);
				l.setMass(Math.random()*255);
				l.setAbsorption(255-l.getMass());
				l.setLocation(random.nextGaussian() * 10 , random.nextGaussian() * 10 );
				l.setLeaderId(-1);
				followers.put(l.getId(), l);
			}	
		}	
		
		interaction.start();
	}


	public static Collection<FollowerAgent> getAllFollowers() {
		Vector<FollowerAgent> robots = new Vector<FollowerAgent>();
		robots.addAll(followers.values());
		return robots;
	}

	public static Collection<LeaderAgent> getAllLeaders() {
		Vector<LeaderAgent> robots = new Vector<LeaderAgent>();
		robots.addAll(leaders.values());
		return robots;
	}

	public static Collection<Agent> getAllRobots() {
		Vector<Agent> robots = new Vector<Agent>();
		robots.addAll(leaders.values());
		robots.addAll(followers.values());
		return robots;
	}

	public static Agent getRobot(short id) {
		if (leaders.containsKey(id))
			return leaders.get(id);
		if (followers.containsKey(id))
			return followers.get(id);
		return null;
	}

	public static Collection<FollowerAgent> getFollowers(short leaderId) {
		Vector<FollowerAgent> ret = new Vector<FollowerAgent>();
		for (FollowerAgent f : followers.values()) {
			if (f.getLeaderId() == leaderId)
				ret.add(f);
		} 
		return ret;
	}


	public static LeaderAgent leader(int id) {
		return leaders.get((short)id);
	}

	public static FollowerAgent follower(int id) {
		return followers.get((short)id);
	}

	public static Target target(int id) {
		return targets.get((short)id);
	}

}
